/**
 * OWASP Enterprise Security API (ESAPI) This file is part of the Open Web
 * Application Security Project (OWASP) Enterprise Security API (ESAPI) project.
 * For details, please see <a
 * href="http://www.owasp.org/index.php/ESAPI">http://
 * www.owasp.org/index.php/ESAPI</a>. Copyright (c) 2007 - The OWASP Foundation
 * The ESAPI is published by OWASP under the BSD license. You should read and
 * accept the LICENSE before you use, modify, and/or redistribute this software.
 * 
 * @author Tarcizio Vieira Neto (tarcizio.vieira@owasp.org) <a href="http://www.serpro.gov.br">SERPRO</a>
 * @created 2011
 */
package br.gov.frameworkdemoiselle.security.filter.wrapper;

import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Vector;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;

import org.owasp.appsensor.AttackDetectorUtils;
import org.owasp.appsensor.errors.AppSensorException;
import org.owasp.esapi.ESAPI;
import org.owasp.esapi.Logger;
import org.owasp.esapi.errors.AccessControlException;
import org.owasp.esapi.errors.ValidationException;
import org.owasp.esapi.filters.SecurityWrapperRequest;

/**
 * This request wrapper simply overrides unsafe methods in the
 * HttpServletRequest API with safe versions that return canonicalized data
 * where possible. The wrapper returns a safe value when a validation error is
 * detected, including stripped or empty strings.
 */
public class BlackListSecurityWrapperRequest extends SecurityWrapperRequest {

	private boolean checkXSS = false;	
	private boolean checkSQLinjection = false;
	
    private final Logger logger = ESAPI.getLogger("SecurityWrapperRequest");

    /** 
     * Construct a safe request that overrides the default request methods with
     * safer versions.
     * 
     * @param request The {@code HttpServletRequest} we are wrapping.
     */
    public BlackListSecurityWrapperRequest(HttpServletRequest request, boolean checkXSS, boolean checkSQLinjection) {
    	super( request );
    	
    	this.checkXSS = checkXSS;
    	this.checkSQLinjection = checkSQLinjection;
    }

    private HttpServletRequest getHttpServletRequest() {
    	return (HttpServletRequest)super.getRequest();
    }
    
    private void checkForBlackList(String param) {
    	if (checkXSS)
    		AttackDetectorUtils.verifyXSSAttack(param);
    	if (checkSQLinjection)
    		AttackDetectorUtils.verifySQLInjectionAttack(param);
    }

    /**
     * Returns the context path from the HttpServletRequest after canonicalizing
     * and filtering out any dangerous characters.
     * @return The context path for this {@code HttpServletRequest}
     */
    public String getContextPath() {
        String path = getHttpServletRequest().getContextPath();

		//Return empty String for the ROOT context
		if (path == null || "".equals(path.trim())) return "";

        String clean = "";
        try {
            clean = ESAPI.validator().getValidInput("HTTP context path: " + path, path, "HTTPContextPath", 150, false);
            //calls black list check for XSS and SQLi
            checkForBlackList(clean);
            
        } catch (ValidationException e) {
            // already logged
        	new AppSensorException("IE2", "Violation of Implemented White List", "The application receives user-supplied data that violates an established white list validation.");
        }
        return clean;
    }

    /**
     * Returns the array of Cookies from the HttpServletRequest after
     * canonicalizing and filtering out any dangerous characters.
     * @return An array of {@code Cookie}s for this {@code HttpServletRequest}
     */
    public Cookie[] getCookies() {
        Cookie[] cookies = getHttpServletRequest().getCookies();
        if (cookies == null) return new Cookie[0];
        
        List<Cookie> newCookies = new ArrayList<Cookie>();
        for (Cookie c : cookies) {
            // build a new clean cookie
            try {
                // get data from original cookie
                String name = ESAPI.validator().getValidInput("Cookie name: " + c.getName(), c.getName(), "HTTPCookieName", 150, true);
                String value = ESAPI.validator().getValidInput("Cookie value: " + c.getValue(), c.getValue(), "HTTPCookieValue", 1000, true);
                int maxAge = c.getMaxAge();
                String domain = c.getDomain();
                String path = c.getPath();
                
                //calls black list check for XSS and SQLi
                checkForBlackList(name);
                checkForBlackList(value);
				
                Cookie n = new Cookie(name, value);
                n.setMaxAge(maxAge);

                if (domain != null) {
                    String safeDomain = ESAPI.validator().getValidInput("Cookie domain: " + domain, domain, "HTTPHeaderValue", 200, false);
                    
                    //calls black list check for XSS and SQLi
                    checkForBlackList(safeDomain);
                	
                    n.setDomain(safeDomain);
                }
                if (path != null) {
                	String safePath = ESAPI.validator().getValidInput("Cookie path: " + path, path, "HTTPHeaderValue", 200, false);
                	
                	//calls black list check for XSS and SQLi
                    checkForBlackList(safePath);
                	
                    n.setPath(safePath);
                }
                newCookies.add(n);                                
            } catch (ValidationException e) {
                logger.warning(Logger.SECURITY_FAILURE, "Skipping bad cookie: " + c.getName() + "=" + c.getValue(), e );
                new AppSensorException("IE2", "Violation of Implemented White List", "The application receives user-supplied data that violates an established white list validation.");
            }
        }
        return newCookies.toArray(new Cookie[newCookies.size()]);
    }


    /**
     * Returns the named header from the HttpServletRequest after canonicalizing
     * and filtering out any dangerous characters.
     * @param name The name of an HTTP request header
     * @return The specified header value is returned.
     */
    public String getHeader(String name) {
        String value = getHttpServletRequest().getHeader(name);
        String clean = "";
        try {
            clean = ESAPI.validator().getValidInput("HTTP header value: " + value, value, "HTTPHeaderValue", 150, true);
            
            //calls black list check for XSS and SQLi
            checkForBlackList(clean);
        } catch (ValidationException e) {
            // already logged
        	new AppSensorException("IE2", "Violation of Implemented White List", "The application receives user-supplied data that violates an established white list validation.");
        }
        return clean;
    }

    /**
     * Returns the enumeration of header names from the HttpServletRequest after
     * canonicalizing and filtering out any dangerous characters.
     * @return An {@code Enumeration} of header names associated with this request.
     */
    public Enumeration getHeaderNames() {
        Vector<String> v = new Vector<String>();
        Enumeration en = getHttpServletRequest().getHeaderNames();
        while (en.hasMoreElements()) {
            try {
                String name = (String) en.nextElement();
                String clean = ESAPI.validator().getValidInput("HTTP header name: " + name, name, "HTTPHeaderName", 150, true);
                v.add(clean);
                
                //calls black list check for XSS and SQLi
                checkForBlackList(clean);
            } catch (ValidationException e) {
                // already logged
            	new AppSensorException("IE2", "Violation of Implemented White List", "The application receives user-supplied data that violates an established white list validation.");
            }
        }
        return v.elements();
    }

    /**
     * Returns the enumeration of headers from the HttpServletRequest after
     * canonicalizing and filtering out any dangerous characters.
     * @param name The name of an HTTP request header.
     * @return An {@code Enumeration} of headers from the request after
     *         canonicalizing and filtering has been performed.
     */
    public Enumeration getHeaders(String name) {
        Vector<String> v = new Vector<String>();
        Enumeration en = getHttpServletRequest().getHeaders(name);
        while (en.hasMoreElements()) {
            try {
                String value = (String) en.nextElement();
                String clean = ESAPI.validator().getValidInput("HTTP header value (" + name + "): " + value, value, "HTTPHeaderValue", 150, true);
                v.add(clean);
                
                //calls black list check for XSS and SQLi
                checkForBlackList(clean);
            } catch (ValidationException e) {
                // already logged
            	new AppSensorException("IE2", "Violation of Implemented White List", "The application receives user-supplied data that violates an established white list validation.");
            }
        }
        return v.elements();
    }

    /**
     * Returns the named parameter from the HttpServletRequest after
     * canonicalizing and filtering out any dangerous characters.
     * @param name The parameter name for the request
     * @return The "scrubbed" parameter value.
     */
    public String getParameter(String name) {
        return getParameter(name, true);
    }

    /**
     * Returns the named parameter from the HttpServletRequest after
     * canonicalizing and filtering out any dangerous characters.
     * @param name The parameter name for the request
     * @param allowNull Whether null values are allowed
     * @return The "scrubbed" parameter value.
     */
    public String getParameter(String name, boolean allowNull) {
        return getParameter(name, allowNull, 2000, "HTTPParameterValue");
    }

    /**
     * Returns the named parameter from the HttpServletRequest after
     * canonicalizing and filtering out any dangerous characters.
     * @param name The parameter name for the request
     * @param allowNull Whether null values are allowed
     * @param maxLength The maximum length allowed
     * @return The "scrubbed" parameter value.
     */
    public String getParameter(String name, boolean allowNull, int maxLength) {
        return getParameter(name,allowNull,maxLength,"HTTPParameterValue");
    }

    /**
     * Returns the named parameter from the HttpServletRequest after
     * canonicalizing and filtering out any dangerous characters.
     * @param name The parameter name for the request
     * @param allowNull Whether null values are allowed
     * @param maxLength The maximum length allowed
     * @param regexName The name of the regex mapped from ESAPI.properties
     * @return The "scrubbed" parameter value.
     */
    public String getParameter(String name, boolean allowNull, int maxLength, String regexName) {
        String orig = getHttpServletRequest().getParameter(name);
        String clean = null;
        try {
            clean = ESAPI.validator().getValidInput("HTTP parameter name: " + name, orig, regexName, maxLength, allowNull);
            
            //calls black list check for XSS and SQLi
            checkForBlackList(clean);
        } catch (ValidationException e) {
            // already logged
        	new AppSensorException("IE2", "Violation of Implemented White List", "The application receives user-supplied data that violates an established white list validation.");
        }
        return clean;
    }

    /**
     * Returns the parameter map from the HttpServletRequest after
     * canonicalizing and filtering out any dangerous characters.
     * @return A {@code Map} containing scrubbed parameter names / value pairs.
     */
    public Map getParameterMap() {
        @SuppressWarnings({"unchecked"})
        Map<String,String[]> map = getHttpServletRequest().getParameterMap();
        Map<String,String[]> cleanMap = new HashMap<String,String[]>();
        for (Object o : map.entrySet()) {
            try {
                Map.Entry e = (Map.Entry) o;
                String name = (String) e.getKey();
                String cleanName = ESAPI.validator().getValidInput("HTTP parameter name: " + name, name, "HTTPParameterName", 100, true);

                String[] value = (String[]) e.getValue();
                String[] cleanValues = new String[value.length];
                for (int j = 0; j < value.length; j++) {
                    String cleanValue = ESAPI.validator().getValidInput("HTTP parameter value: " + value[j], value[j], "HTTPParameterValue", 2000, true);
                    cleanValues[j] = cleanValue;
                    
                    //calls black list check for XSS and SQLi
                    checkForBlackList(cleanValue);
                }
                cleanMap.put(cleanName, cleanValues);
            } catch (ValidationException e) {
                // already logged
            	new AppSensorException("IE2", "Violation of Implemented White List", "The application receives user-supplied data that violates an established white list validation.");
            }
        }
        return cleanMap;
    }

    /**
     * Returns the enumeration of parameter names from the HttpServletRequest
     * after canonicalizing and filtering out any dangerous characters.
     * @return An {@code Enumeration} of properly "scrubbed" parameter names.
     */
    public Enumeration getParameterNames() {
        Vector<String> v = new Vector<String>();
        Enumeration en = getHttpServletRequest().getParameterNames();
        while (en.hasMoreElements()) {
            try {
                String name = (String) en.nextElement();
                String clean = ESAPI.validator().getValidInput("HTTP parameter name: " + name, name, "HTTPParameterName", 150, true);
                v.add(clean);
                
                //needs Black List check?
            } catch (ValidationException e) {
                // already logged
            	new AppSensorException("IE2", "Violation of Implemented White List", "The application receives user-supplied data that violates an established white list validation.");
            }
        }
        return v.elements();
    }

    /**
     * Returns the array of matching parameter values from the
     * HttpServletRequest after canonicalizing and filtering out any dangerous
     * characters.
     * @param name The parameter name
     * @return An array of matching "scrubbed" parameter values or
     * <code>null</code> if the parameter does not exist.
     */
    public String[] getParameterValues(String name) {
        String[] values = getHttpServletRequest().getParameterValues(name);
        List<String> newValues;

	if(values == null)
		return null;
        newValues = new ArrayList<String>();
        for (String value : values) {
            try {
                String cleanValue = ESAPI.validator().getValidInput("HTTP parameter value: " + value, value, "HTTPParameterValue", 2000, true);
                newValues.add(cleanValue);
                
                //calls black list check for XSS and SQLi
                checkForBlackList(cleanValue);
            } catch (ValidationException e) {
                logger.warning(Logger.SECURITY_FAILURE, "Skipping bad parameter");
            }
        }
        return newValues.toArray(new String[newValues.size()]);
    }

    /**
     * Returns the path info from the HttpServletRequest after canonicalizing
     * and filtering out any dangerous characters.
     * @return Returns any extra path information, appropriately scrubbed,
     *         associated with the URL the client sent when it made this request.
     */
    public String getPathInfo() {
        String path = getHttpServletRequest().getPathInfo();
		if (path == null) return null;
        String clean = "";
        try {
            clean = ESAPI.validator().getValidInput("HTTP path: " + path, path, "HTTPPath", 150, true);
            
            //needs Black List check?
        } catch (ValidationException e) {
            // already logged
        }
        return clean;
    }

    /**
     * Returns the query string from the HttpServletRequest after canonicalizing
     * and filtering out any dangerous characters.
     * @return The scrubbed query string is returned.
     */
    public String getQueryString() {
        String query = getHttpServletRequest().getQueryString();
        String clean = "";
        try {
            clean = ESAPI.validator().getValidInput("HTTP query string: " + query, query, "HTTPQueryString", 2000, true);
            
            //calls black list check for XSS and SQLi
            checkForBlackList(clean);
        } catch (ValidationException e) {
            // already logged
        }
        return clean;
    }

    /**
     * Returns the URI from the HttpServletRequest after canonicalizing and
     * filtering out any dangerous characters. Code must be very careful not to
     * depend on the value of a requested session id reported by the user.
     * @return The requested Session ID
     */
    public String getRequestedSessionId() {
        String id = getHttpServletRequest().getRequestedSessionId();
        String clean = "";
        try {
            clean = ESAPI.validator().getValidInput("Requested cookie: " + id, id, "HTTPJSESSIONID", 50, false);
            
            //needs Black List check?
        } catch (ValidationException e) {
            // already logged
        }
        return clean;
    }

    /**
     * Returns the URI from the HttpServletRequest after canonicalizing and
     * filtering out any dangerous characters.
     * @return The current request URI
     */
    public String getRequestURI() {
        String uri = getHttpServletRequest().getRequestURI();
        String clean = "";
        try {
            clean = ESAPI.validator().getValidInput("HTTP URI: " + uri, uri, "HTTPURI", 2000, false);
            
            //needs Black List check?
        } catch (ValidationException e) {
            // already logged
        }
        return clean;
    }

    /**
     * Returns the URL from the HttpServletRequest after canonicalizing and
     * filtering out any dangerous characters.
     * @return The currect request URL
     */
    public StringBuffer getRequestURL() {
        String url = getHttpServletRequest().getRequestURL().toString();
        String clean = "";
        try {
            clean = ESAPI.validator().getValidInput("HTTP URL: " + url, url, "HTTPURL", 2000, false);
            
            //calls black list check for XSS and SQLi
            checkForBlackList(clean);
        } catch (ValidationException e) {
            // already logged
        }
        return new StringBuffer(clean);
    }

    /**
     * Returns the scheme from the HttpServletRequest after canonicalizing and
     * filtering out any dangerous characters.
     * @return The scheme of the current request
     */
    public String getScheme() {
        String scheme = getHttpServletRequest().getScheme();
        String clean = "";
        try {
            clean = ESAPI.validator().getValidInput("HTTP scheme: " + scheme, scheme, "HTTPScheme", 10, false);
            
            //needs Black List check?
        } catch (ValidationException e) {
            // already logged
        }
        return clean;
    }

    /**
     * Returns the server name (host header) from the HttpServletRequest after
     * canonicalizing and filtering out any dangerous characters.
     * @return The local server name
     */
    public String getServerName() {
        String name = getHttpServletRequest().getServerName();
        String clean = "";
        try {
            clean = ESAPI.validator().getValidInput("HTTP server name: " + name, name, "HTTPServerName", 100, false);
            
            //needs Black List check?
        } catch (ValidationException e) {
            // already logged
        }
        return clean;
    }
	

    /**
     * Returns the server path from the HttpServletRequest after canonicalizing
     * and filtering out any dangerous characters.
     * @return The servlet path
     */
    public String getServletPath() {
        String path = getHttpServletRequest().getServletPath();
        String clean = "";
        try {
            clean = ESAPI.validator().getValidInput("HTTP servlet path: " + path, path, "HTTPServletPath", 100, false);
            
            //calls black list check for XSS and SQLi
            checkForBlackList(clean);            
        } catch (ValidationException e) {
            // already logged
        }
        return clean;
    }


    /**
     * Same as HttpServletRequest, no security changes required.
     * @return Whether the current request is secure
     */
    public boolean isSecure() {
        try {
            ESAPI.httpUtilities().assertSecureChannel();
        } catch (AccessControlException e) {        	        	
            return false;
        }
        return true;
    }

}